對有向無環圖(DAG),存在一個節點排列,使得每條邊 u → v 皆滿足 u 在 v 之前。
若圖中有有向環,則拓撲序不存在。
原題:
https://cses.fi/problemset/task/1679
題意
給 n 門課(1…n)與 m 條先修關係 a → b(修 a 才能修 b)。
請輸出一個可行的修課順序;若不存在(有向環)則輸出 IMPOSSIBLE。
解題思路
#include <bits/stdc++.h>
using namespace std;
int main(){
    int n, m;
    cin >> n >> m;
    vector<vector<int>> g(n+1);
    vector<int> indeg(n+1, 0);
    while(m--){
        int a, b; cin >> a >> b; // a -> b
        g[a].push_back(b);
        indeg[b]++;
    }
    queue<int> q;
    for(int i=1;i<=n;i++) if(indeg[i]==0) q.push(i);
    vector<int> order;
    while(!q.empty()){
        int u = q.front(); q.pop();
        order.push_back(u);
        for(int v: g[u]){
            if(--indeg[v]==0) q.push(v);
        }
    }
    if((int)order.size()!=n){
        cout << "IMPOSSIBLE\n";
    }else{
        for(int i=0;i<n;i++)
            cout << order[i] << (i+1==n?'\n':' ');
    }
    return 0;
}
原題:
https://cses.fi/problemset/task/1681
題意:
給定一張有向無環圖(DAG),求從節點 1 到節點 n 的不同路徑數
解題思路:
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1'000'000'007;
int main(){
    int n, m;
    cin >> n >> m;
    vector<vector<int>> g(n+1);
    vector<int> indeg(n+1, 0);
    while(m--){
        int a,b; cin >> a >> b;
        g[a].push_back(b);
        indeg[b]++;
    }
    queue<int> q;
    for(int i=1;i<=n;i++) if(indeg[i]==0) q.push(i);
    vector<long long> dp(n+1, 0);
    dp[1] = 1;
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int v: g[u]){
            dp[v] += dp[u];
            if(dp[v] >= MOD) dp[v] -= MOD;
            if(--indeg[v]==0) q.push(v);
        }
    }
    cout << dp[n] % MOD << "\n";
    return 0;
}
原題:
https://leetcode.com/problems/course-schedule/description/
題意:
numCourses 門課(0…n-1)與先修關係 prerequisites[i] = [a, b](修 b 才能修 a)。
問是否能修完全部課(是否存在拓撲序)。
解題思路:
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> g(numCourses);
        vector<int> indeg(numCourses, 0);
        for(auto &e: prerequisites){
            int a=e[0], b=e[1]; // b -> a
            g[b].push_back(a);
            indeg[a]++;
        }
        queue<int> q;
        for(int i=0;i<numCourses;i++) if(indeg[i]==0) q.push(i);
        int taken = 0;
        while(!q.empty()){
            int u = q.front(); q.pop();
            taken++;
            for(int v: g[u]){
                if(--indeg[v]==0) q.push(v);
            }
        }
        return taken == numCourses;
    }
};
原題:
https://leetcode.com/problems/course-schedule-ii/
題意:
同上題,但需要輸出一個可行的修課順序。若不存在(有環)回傳空陣列。
解題思路:
class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> g(numCourses);
        vector<int> indeg(numCourses, 0);
        for(auto &e: prerequisites){
            int a=e[0], b=e[1]; // b -> a
            g[b].push_back(a);
            indeg[a]++;
        }
        queue<int> q;
        for(int i=0;i<numCourses;i++) if(indeg[i]==0) q.push(i);
        vector<int> order;
        while(!q.empty()){
            int u = q.front(); q.pop();
            order.push_back(u);
            for(int v: g[u]){
                if(--indeg[v]==0) q.push(v);
            }
        }
        if((int)order.size()!=numCourses) return {};
        return order;
    }
};